# ruff: noqa: E501
import os
from datetime import date
from string import Template

import typer
from pydash import pascal_case, snake_case

from guardrails.cli.hub.hub import hub_command
from guardrails.cli.logger import LEVELS, logger
from guardrails.hub_telemetry.hub_tracing import trace

validator_template = Template(
    """
\"""
This template is intended for creating simple validators.

If your validator is complex or requires additional post-installation steps, consider using the template repository instead.

The template repository can be found here: https://github.com/guardrails-ai/validator-template
\"""
    
from typing import Any, Callable, Dict, Optional

from guardrails.validator_base import (
    FailResult,
    PassResult,
    ValidationResult,
    Validator,
    register_validator,
)


@register_validator(name="guardrails/${package_name}", data_type="string")
class ${class_name}(Validator):
    # FIXME: Update the class docstring to reflect the purpose and usage of your validator.
    \"""# Overview

    | Developed by | {FIXME: Your organization name} |
    | Date of development | ${dev_date} |
    | Validator type | Format |
    | License | Apache 2 |
    | Input/Output | Output |

    # Description
    {FIXME: A brief description of what your validator does.}
    
    ## (Optional) Intended Use
    {FIXME: Optionally, include a brief description of the intended use of your validator, including any limitations or constraints.}

    ## Requirements

    * Dependencies:
        - guardrails-ai>=0.4.0
        - {FIXME: Include any other dependencies you need here}

    * Dev Dependencies:
        - pytest
        - pyright
        - ruff
        - {FIXME: Include any other dev dependencies you need here}

    * Foundation model access keys:
        - {FIXME: Include any access environment variables you need here like OPENAI_API_KEY}


    # Installation

    ```bash
    $ guardrails hub install hub://guardrails/${package_name}
    ```

    # Usage Examples

    ## Validating string output via Python

    In this example, we apply the validator to a string output generated by an LLM.

    ```python
    # Import Guard and Validator
    from guardrails.hub import ${class_name}
    from guardrails import Guard

    # Setup Guard
    guard = Guard.use(
        ${class_name}({FIXME: list any args here})
    )

    guard.validate({FIXME: Add an input that should pass the validator})  # Validator passes
    guard.validate({FIXME: Add an input that should fail the validator})  # Validator fails
    ```
    \"""  # noqa

    # If you don't have any init args, you can omit the __init__ method.
    def __init__(
        self,
        arg_1: str, # FIXME: Replace with your custom init args.
        on_fail: Optional[Callable] = None,
    ):
        \"""Initializes a new instance of the ${class_name} class.
        
        Args:
            arg_1 (str): FIXME: Describe the purpose of this argument.
            on_fail`** *(str, Callable)*: The policy to enact when a validator fails.  If `str`, must be one of `reask`, `fix`, `filter`, `refrain`, `noop`, `exception` or `fix_reask`. Otherwise, must be a function that is called when the validator fails.
        \"""
        super().__init__(on_fail=on_fail, arg_1=arg_1)
        self._arg_1 = arg_1

    def validate(self, value: Any, metadata: Dict) -> ValidationResult:
        \"""Validates that {fill in how you validator interacts with the passed value}.
        
        Args:
            value (Any): The value to validate.
            metadata (Dict): The metadata to validate against.

            FIXME: Add any additional args you need here in metadata.
            | Key | Description |
            | --- | --- |
            | a | b |
        \"""
        
        # Add your custom validator logic here and return a PassResult or FailResult accordingly.
        if value != "pass": # FIXME
            return FailResult(
                error_message="{FIXME: A descriptive but concise error message about why validation failed}",
                fix_value="{FIXME: The programmtic fix if applicable, otherwise remove this kwarg.}",
            )
        return PassResult()


# Run tests via `pytest -rP ${filepath}`
class Test${class_name}:
    def test_success_case(self):
        # FIXME: Replace with your custom test logic for the success case.
        validator = ${class_name}("s")
        result = validator.validate("pass", {})
        assert isinstance(result, PassResult) is True

    def test_failure_case(self):
        # FIXME: Replace with your custom test logic for the failure case.
        validator = ${class_name}("s")
        result = validator.validate("fail", {})
        assert isinstance(result, FailResult) is True
        assert result.error_message == "{A descriptive but concise error message about why validation failed}"
        assert result.fix_value == "fails"
"""
)


@hub_command.command(name="create-validator")
@trace(name="guardrails-cli/hub/create-validator")
def create_validator(
    name: str = typer.Argument(help="The name for your validator."),
    filepath: str = typer.Argument(
        help="The location to write your validator template to",
        default="./{validator_name}.py",
    ),
):
    """Lightweight method for creating simple validators.

    For more complex submissions see here:
    https://github.com/guardrails-ai/validator-template?tab=readme-ov-file#how-to-create-a-guardrails-validator
    """
    disclaimer = """

    This utility is intended for creating simple validators.

    If your validator is complex or requires additional post-installation steps,\
        consider using the template repository instead.

    The template repository can be found here:\
        https://github.com/guardrails-ai/validator-template
    """
    logger.log(level=LEVELS.get("NOTICE") or 0, msg=disclaimer)

    package_name = snake_case(name)
    class_name = pascal_case(name)
    if not filepath or filepath == "./{validator_name}.py":
        filepath = f"./{package_name}.py"

    template = validator_template.safe_substitute(
        {
            "package_name": package_name,
            "class_name": class_name,
            "filepath": filepath,
            "dev_date": date.today().strftime("%b %d, %Y"),
        }
    )

    target = os.path.abspath(filepath)
    with open(target, "w") as validator_file:
        validator_file.write(template)
        validator_file.close()

    success_message = Template(
        """

    Successfully created validator template at ${filepath}!

    Make any necessary changes then submit for review with the following command:

    guardrails hub submit ${package_name} ${filepath}
    """
    ).safe_substitute({"filepath": filepath, "package_name": package_name})
    logger.log(level=LEVELS.get("SUCCESS"), msg=success_message)  # type: ignore
